package com.jeffers.videorecord;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.WindowManager;
import android.widget.FrameLayout;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * A helper class to show fragment as whole content view for the host activity.
 * Note that this activity will add the first fragment to activity without addToBackStack.
 * 
 * @author AlfredZhong
 * @version 2014-07-29
 */
public class FragmentContainerActivity extends AppCompatActivity {

    private static final String TAG = FragmentContainerActivity.class.getSimpleName();
    // external extra
    private static final String EXTRA_ACTIVITY_INTENT_FLAG = "activity_intent_flag";
    // internal extra
    private static final String EXTRA_ACTIVITY_NO_TITLE = "activity_no_title";
    private static final String EXTRA_ACTIVITY_FULL_SCREEN = "activity_full_screen";
    private static final String EXTRA_ROOT_FRAGMENT_CLASS = "root_fragment_class";
    private static final String EXTRA_FRAGMENT_ARGUMENTS = "root_fragment_arguments";
    private static final String EXTRA_FRAGMENT_CLASS_LIST = "fragment_class_list";
    private static final String EXTRA_FRAGMENT_ARGUMENTS_LIST = "fragment_arguments_list";

    @SuppressWarnings("unchecked")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState != null) {
            // Fragments will be restored along with Activity recreated.
            return;
        }
        Bundle extras = getIntent().getExtras();
        if (extras == null) {
            throw new RuntimeException("Unexpected exception occured when activity onCreate() : activity extras is null.");
        }
        if (extras.getBoolean(EXTRA_ACTIVITY_NO_TITLE, false)) {
            requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);
        }
        if (extras.getBoolean(EXTRA_ACTIVITY_FULL_SCREEN, false)) {
            getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        }
        // setTheme() is too buggy. Don't setTheme() at runtime.
        setContentView(new FrameLayout(this));
        Class<? extends Fragment> root = (Class<? extends Fragment>) extras.getSerializable(EXTRA_ROOT_FRAGMENT_CLASS);
        Log.d(TAG, EXTRA_ROOT_FRAGMENT_CLASS + " " + root);
        // set the root fragment as content view.
        replace(this, root, extras.getBundle(EXTRA_FRAGMENT_ARGUMENTS), null);
        // check following fragments.
        Serializable fragmentListSerializable = extras.getSerializable(EXTRA_FRAGMENT_CLASS_LIST);
        if (fragmentListSerializable == null) {
            return;
        }
        List<Class<? extends Fragment>> fragmentList = (List<Class<? extends Fragment>>) fragmentListSerializable;
        Log.d(TAG, EXTRA_FRAGMENT_CLASS_LIST + " " + fragmentList);
        if (fragmentList != null) {
            List<Bundle> argumentList = extras.getParcelableArrayList(EXTRA_FRAGMENT_ARGUMENTS_LIST);
            int fragmentListSize = fragmentList.size();
            int argumentListSize = argumentList != null ? argumentList.size() : 0;
            for (int i = 0; i < fragmentListSize; i++) {
                replaceWithBackStack(this, fragmentList.get(i), argumentListSize > i ? argumentList.get(i) : null, null);
            }
        }
    }

    public static interface BackPressedSupport {
        public boolean onHostActivityBackPressed();
    }

    @Override
    public void onBackPressed() {
        FragmentManager fm = getSupportFragmentManager();
        Fragment currentShowingFragment = fm.findFragmentById(android.R.id.content);
        // System.out.println(TAG + ".onBackPressed() current fragment : " + currentShowingFragment);
        BackPressedSupport support = null;
        if (currentShowingFragment instanceof BackPressedSupport) {
            support = (BackPressedSupport) currentShowingFragment;
        }
        if (support == null) {
            Log.d(TAG, "onBackPressed() : fragment is not BackPressedSupport, super handle it.");
            super.onBackPressed();
            return;
        }
        // if handled returns true, do nothing; returns false call super.onBackPressed().
        if (support.onHostActivityBackPressed() == false) {
            Log.d(TAG, "onBackPressed() : fragment not handled BACK event, super handle it.");
            super.onBackPressed();
            return;
        }
        Log.i(TAG, "onBackPressed() : fragment handled BACK event, do nothing.");
        // int index = fm.getBackStackEntryCount() - 1;
        // fm.popBackStack(fm.getBackStackEntryAt(index).getId(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
    }

    public static void startActivity(FragmentActivity from, Class<? extends Fragment> to, Bundle extras) {
        startActivityInternal(from, to, extras, Integer.MIN_VALUE);
    }

    public static void startActivity(Fragment from, Class<? extends Fragment> to, Bundle extras) {
        startActivityInternal(from, to, extras, Integer.MIN_VALUE);
    }

    public static void startActivity(FragmentActivity from, Class<? extends Fragment> to, Bundle extras, int requestCode) {
        startActivityInternal(from, to, extras, requestCode);
    }

    public static void startActivity(Fragment from, Class<? extends Fragment> to, Bundle extras, int requestCode) {
        startActivityInternal(from, to, extras, requestCode);
    }

    public static void fillBundleWithActivityIntentFlags(Bundle extras, int flags) {
        // e.g. int flags = Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK;
        extras.putInt(FragmentContainerActivity.EXTRA_ACTIVITY_INTENT_FLAG, flags);
    }

    public static void fillBundleWithActivityNoTitle(Bundle extras) {
        extras.putBoolean(FragmentContainerActivity.EXTRA_ACTIVITY_NO_TITLE, true);
    }

    public static void fillBundleWithActivityFullScreen(Bundle extras) {
        extras.putBoolean(FragmentContainerActivity.EXTRA_ACTIVITY_FULL_SCREEN, true);
    }

    public static void fillBundleWithFragmentArguments(Bundle extras, Bundle fragmentArguments) {
        extras.putBundle(FragmentContainerActivity.EXTRA_FRAGMENT_ARGUMENTS, fragmentArguments);
    }

    public static void fillBundleWithFragmentList(Bundle extras, ArrayList<Class<? extends Fragment>> fragmentList,
            ArrayList<Bundle> argumentsList) {
        extras.putSerializable(FragmentContainerActivity.EXTRA_FRAGMENT_CLASS_LIST, (Serializable) fragmentList);
        ArrayList<? extends android.os.Parcelable> parcelableList = (ArrayList<? extends android.os.Parcelable>) argumentsList;
        extras.putParcelableArrayList(FragmentContainerActivity.EXTRA_FRAGMENT_ARGUMENTS_LIST, parcelableList);
    }

    /*
        Bundle extras = new Bundle();
        FragmentContainerActivity.fillBundleWithActivityIntentFlags(extras, flags);
        FragmentContainerActivity.fillBundleWithActivityFullScreen(extras);
        FragmentContainerActivity.fillBundleWithActivityNoTitle(extras);
        FragmentContainerActivity.fillBundleWithFragmentArguments(extras, fragmentArguments);
        ArrayList<Class<? extends Fragment>> fragmentList = new ArrayList<Class<? extends Fragment>>();
        ArrayList<Bundle> argumentsList = new ArrayList<Bundle>();
        fragmentList.add(YFragment.class);
        argumentsList.add(null);
        fragmentList.add(ZFragment.class);
        argumentsList.add(new Bundle());
        FragmentContainerActivity.fillBundleWithFragmentList(extras, fragmentList, argumentsList);
        FragmentContainerActivity.startActivity(this, XFragment.class, extras);
     */
    private static void startActivityInternal(Object from, Class<? extends Fragment> to, Bundle extras, int requestCode) {
        if (from == null || to == null) {
            throw new IllegalArgumentException(TAG + " from and to args can not be null.");
        }
        FragmentActivity fromActivity = null;
        Fragment fromFragment = null;
        if (from instanceof FragmentActivity) {
            fromActivity = (FragmentActivity) from;
        } else if (from instanceof Fragment) {
            fromFragment = (Fragment) from;
        } else {
            throw new IllegalArgumentException(TAG + " from can only be FragmentActivity or Fragment.");
        }
        Context context = fromActivity != null ? fromActivity : fromFragment.getActivity();
        Intent intent = new Intent(context, FragmentContainerActivity.class);
        if (extras == null) {
            extras = new Bundle();
        }
        extras.putSerializable(EXTRA_ROOT_FRAGMENT_CLASS, to);
        intent.putExtras(extras);
        if (extras != null) {
            int flags = extras.getInt(EXTRA_ACTIVITY_INTENT_FLAG, Integer.MIN_VALUE);
            if (flags != Integer.MIN_VALUE) {
                intent.addFlags(flags);
            }
        }
        if (requestCode != Integer.MIN_VALUE) {
            if (fromActivity != null) {
                fromActivity.startActivityForResult(intent, requestCode);
            } else {
                fromFragment.startActivityForResult(intent, requestCode);
            }
        } else {
            if (fromActivity != null) {
                fromActivity.startActivity(intent);
            } else {
                fromFragment.startActivity(intent);
            }
        }
    }

    public static void replace(FragmentActivity act, Class<? extends Fragment> clazz, Bundle args, String tag) {
        Fragment fragment = Fragment.instantiate(act, clazz.getName());
        if (args != null) {
            fragment.setArguments(args);
        } else {
            fragment.setArguments(new Bundle());
        }
        FragmentManager fm = act.getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        ft.replace(android.R.id.content, fragment, tag);
        ft.commit();
        fm.executePendingTransactions();
    }

    public static void replaceWithBackStack(FragmentActivity act, Class<? extends Fragment> clazz, Bundle args, String tag) {
        Fragment fragment = Fragment.instantiate(act, clazz.getName());
        if (args != null) {
            fragment.setArguments(args);
        } else {
            fragment.setArguments(new Bundle());
        }
        FragmentManager fm = act.getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        // ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
        // ft.setCustomAnimations(enter, exit);
        // ft.setCustomAnimations(enter, exit, popEnter, popExit);
        ft.replace(android.R.id.content, fragment, tag);
        ft.addToBackStack(null);
        ft.commit();
        fm.executePendingTransactions();
    }

    /**
     * Pop current fragment.
     * 
     * @param act
     * @return true if pop success, false if pop failed.
     */
    public static boolean popFragment(FragmentActivity act) {
        FragmentManager fm = act.getSupportFragmentManager();
        int count = fm.getBackStackEntryCount();
        if (count > 0) {
            fm.popBackStack();
            return true;
        } else {
            Log.w(TAG, "popFragment failed, count : " + count);
            return false;
        }
    }

    /**
     * Pop fragments.
     * 
     * @param act 
     * @param indexOfBackStackEntry the index in the BackStackEntry; start index 0 being the bottom of the stack.
     * @param includingSelfIndex true will pop fragment from index including itself, false not including itself.
     * @return true if pop success, false if pop failed.
     */
    public static boolean popFragment(FragmentActivity act, int indexOfBackStackEntry, boolean includingSelfIndex) {
        if (indexOfBackStackEntry < 0) {
            return false;
        }
        FragmentManager fm = act.getSupportFragmentManager();
        int count = fm.getBackStackEntryCount();
        if (count > indexOfBackStackEntry) {
            // enough to pop.
            // POP_BACK_STACK_INCLUSIVE will pop fragment from index(including itself)
            // If you don't want to pop index fragment itself, use popBackStack(id, 0);
            int flags = includingSelfIndex ? FragmentManager.POP_BACK_STACK_INCLUSIVE : 0;
            fm.popBackStack(fm.getBackStackEntryAt(indexOfBackStackEntry).getId(), flags);
            return true;
        } else {
            Log.w(TAG, "popFragment failed, count : " + count + ", index : " + indexOfBackStackEntry);
            return false;
        }
    }

}
